`define IREQ_W 103
`define IDATA_W 132

module isram_like_wrap(
  input clk,
  input rst,

  // from pc
  input  valid_i,
  output stall_o,

  input [31:0] paddr_i,
  input [31:0] vaddr_i,
  input        br_pre_taken_i,
  input [31:0] br_pre_pc_i,
  input [1:0] br_offset_i,
  input        uncache_i,
  input        ft_tlbre_i,
  input        ft_tlbi_i,
  input        ft_adel_i,

  // to instr queue
  output valid_o,
  input  stall_i,
  input  flush_i,
  input br_taken,
  input [31:0] br_ds_pc,

  output [ 31:0] pc_o,
  output [127:0] instr_o,
  output [  3:0] rstrb_o,
  output         br_pre_taken_o,
  output [31:0]  br_pre_pc_o   ,
  output [1:0] br_offset_o,
  output         ft_tlbre_o,
  output         ft_tlbi_o,
  output         ft_adel_o,

  // inst sram-like interface
  output        inst_sram_req    ,
  input         inst_sram_addr_ok,
  output        inst_sram_uncache,
  output [31:0] inst_sram_addr   ,

  input        inst_sram_data_ok,
  input [  3:0] inst_sram_rstrb,
  input [127:0] inst_sram_rdata
);

  wire irb_wvalid;
  wire irb_wready;
  wire [`IREQ_W-1:0] irb_wdata;
  wire irb_rvalid;
  wire irb_rready; 
  wire [`IREQ_W-1:0] irb_rdata;
  wire irb_pvalid;
  wire irb_pready;
  wire irb_pcancel;
  wire [`IREQ_W-1:0] irb_pdata;

  // write in idb only when 
  // 1. instr buffer is full
  // 2. flush taken but instr fetch back
  // 3. idb is poping instruction
  wire idb_wvalid;
  wire idb_wready;
  wire [`IDATA_W - 1:0] idb_wdata;
  wire idb_pvalid;
  wire idb_pready; 
  wire [`IDATA_W - 1:0] idb_pdata;
  wire [127:0] idb_pinstr;
  wire [3:0] idb_prstrb;

  wire [31:0] irb_rpaddr;
  wire [31:0] irb_rvaddr;
  wire irb_rbr_pre_taken;
  wire [31:0] irb_rbr_pre_pc;
  wire [1:0] irb_rbr_offset;
  wire irb_runcache;
  wire irb_rft_adel;
  wire irb_rft_tlbre;
  wire irb_rft_tlbi;

  wire [31:0] irb_ppaddr;
  wire [31:0] irb_pvaddr;
  wire irb_pbr_pre_taken;
  wire [31:0] irb_pbr_pre_pc;
  wire [1:0] irb_pbr_offset;
  wire irb_puncache;
  wire irb_pft_adel;
  wire irb_pft_tlbre;
  wire irb_pft_tlbi;

  assign irb_wvalid = valid_i;
  assign idb_wvalid = inst_sram_data_ok & (stall_i | idb_pvalid);
  assign idb_wdata = {
    inst_sram_rstrb,
    inst_sram_rdata
  };
  assign idb_pready = ~stall_i & irb_pvalid & ~(irb_pft_adel | irb_pft_tlbre | irb_pft_tlbi);

  assign irb_wdata = {
    paddr_i   ,
    vaddr_i   ,
    br_pre_taken_i,
    br_pre_pc_i,
    br_offset_i,
    uncache_i ,
    ft_adel_i ,
    ft_tlbre_i,
    ft_tlbi_i 
  };
  assign irb_rready = 
    inst_sram_addr_ok | 
    (irb_rft_adel | irb_rft_tlbre | irb_rft_tlbi) & irb_rvalid;
  assign irb_pready = 
    ~stall_i & (
      irb_pvalid & idb_pvalid & ~(irb_pft_adel | irb_pft_tlbre | irb_pft_tlbi) |
      irb_pvalid              &  (irb_pft_adel | irb_pft_tlbre | irb_pft_tlbi) |
      irb_pvalid & inst_sram_data_ok
    );

  assign {
    irb_rpaddr,
    irb_rvaddr,
    irb_rbr_pre_taken,
    irb_rbr_pre_pc,
    irb_rbr_offset,
    irb_runcache,
    irb_rft_adel,
    irb_rft_tlbre,
    irb_rft_tlbi
  } = irb_rdata;

  assign {
    irb_ppaddr,
    irb_pvaddr,
    irb_pbr_pre_taken,
    irb_pbr_pre_pc,
    irb_pbr_offset,
    irb_puncache,
    irb_pft_adel,
    irb_pft_tlbre,
    irb_pft_tlbi
  } = irb_pdata;

  assign {
    idb_prstrb,
    idb_pinstr
  } = idb_pdata;

  assign stall_o = ~irb_wready;

  // delay slot is not influenced by cancel bit
  assign valid_o =
    (
      irb_pvalid & idb_pvalid & ~(irb_pft_adel | irb_pft_tlbre | irb_pft_tlbi) |
      irb_pvalid              &  (irb_pft_adel | irb_pft_tlbre | irb_pft_tlbi) |
      irb_pvalid & inst_sram_data_ok
    ) & ~irb_pcancel;
  assign pc_o = irb_pvaddr;
  // select rdata when idb pop is not valid (use inst sram interface return data directly)
  // FIXME:
  assign instr_o = idb_pvalid ? idb_pinstr : inst_sram_rdata;  
  assign rstrb_o = idb_pvalid ? idb_prstrb : inst_sram_rstrb;
  assign br_pre_taken_o = irb_pbr_pre_taken;
  assign br_pre_pc_o = irb_pbr_pre_pc;
  assign br_offset_o = irb_pbr_offset;
  assign ft_adel_o = irb_pft_adel;
  assign ft_tlbre_o = irb_pft_tlbre;
  assign ft_tlbi_o = irb_pft_tlbi;

  assign inst_sram_req = irb_rvalid & ~(irb_rft_adel | irb_rft_tlbre & irb_rft_tlbi);
  assign inst_sram_uncache = irb_runcache;
  assign inst_sram_addr = irb_rpaddr;

  irb#(
    .DEPTH(2),
    .WIDTH(`IREQ_W)
  ) mycpu_irb (
    .clk(clk),
    .rst(rst),
    .flush(flush_i),

    .br_taken(br_taken),
    .br_ds_pc(br_ds_pc),

    .wvalid(irb_wvalid),
    .wready(irb_wready),
    .wdata (irb_wdata ),

    .rvalid(irb_rvalid),
    .rready(irb_rready),
    .rdata (irb_rdata ),

    .pvalid (irb_pvalid ),
    .pready (irb_pready ),
    .pcancel(irb_pcancel),
    .pdata  (irb_pdata  )
  );

  idb#(
    .DEPTH(2),
    .WIDTH(`IDATA_W)
  ) mycpu_idb (
    .clk(clk),
    .rst(rst),

    .wvalid(idb_wvalid),
    .wready(idb_wready),
    .wdata (idb_wdata ),

    .pvalid(idb_pvalid),
    .pready(idb_pready),
    .pdata (idb_pdata )
  );

endmodule